#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/proc_fs.h>
#include <linux/list.h>
#include <asm/uaccess.h>
#include <linux/udp.h>
#include <linux/tcp.h>
#include <linux/skbuff.h>
#include <linux/ip.h>
#include <linux/netfilter.h>
#include <linux/netfilter_ipv4.h>
#include "Definitii.h"

//functia care va face hooking-ul pentru pachetele care ies.

unsigned int hook_func_out(unsigned int hooknum, struct sk_buff *skb, 
        const struct net_device *in, const struct net_device *out,
        int (*okfn)(struct sk_buff *)) 
{
	struct iphdr *ip_header = (struct iphdr *)skb_network_header(skb);	
	struct udphdr *udp_header;
    struct tcphdr *tcp_header;
	struct list_head *p;
    struct regula_firewall *a_rule;
    char src_ip_str[16], dest_ip_str[16];
    int i = 0;
    unsigned int src_ip = (unsigned int)ip_header->saddr;
    unsigned int dest_ip = (unsigned int)ip_header->daddr;
    unsigned int src_port = 0;
    unsigned int dest_port = 0;
    if (ip_header->protocol==17) 
	{
       udp_header = (struct udphdr *)skb_transport_header(skb);
       src_port = (unsigned int)ntohs(udp_header->source);
       dest_port = (unsigned int)ntohs(udp_header->dest);
   } 
   else
	   if (ip_header->protocol == 6) 
	   {
		   tcp_header = (struct tcphdr *)skb_transport_header(skb);
		   src_port = (unsigned int)ntohs(tcp_header->source);
		   dest_port = (unsigned int)ntohs(tcp_header->dest);
	   }

   ip_hl_to_str(ntohl(src_ip), src_ip_str);

   ip_hl_to_str(ntohl(dest_ip), dest_ip_str);

 

   printk(KERN_INFO "OUT packet info: src ip: %u = %s, src port: %u; dest ip: %u = %s, dest port: %u; proto: %u\n", src_ip, src_ip_str, src_port, dest_ip, dest_ip_str, dest_port, ip_header->protocol); 

 
   //verifica daca in firewall exista vreo potrivire.
   //in cazul in care exista mai multe reguli care se potrivesc ia-o va fi folosita prima.
   list_for_each(p, &regula.list)
   {
       i++;
       a_rule = list_entry(p, struct regula_firewall, list);
       //printk(KERN_INFO "rule %d: a_rule->in_out = %u; a_rule->src_ip = %u; a_rule->src_netmask=%u; a_rule->src_port=%u; a_rule->dest_ip=%u; a_rule->dest_netmask=%u; a_rule->dest_port=%u; a_rule->proto=%u; a_rule->action=%u\n", i, a_rule->in_out, a_rule->src_ip, a_rule->src_netmask, a_rule->src_port, a_rule->dest_ip, a_rule->dest_netmask, a_rule->dest_port, a_rule->proto, a_rule->action);
       //daca o regula nu este marcata ca fiind "out", ea va fi omisa
       if (a_rule->in_out != 2) 
	   {
           printk(KERN_INFO "regula %d (a_rule->in_out: %u)nu se potriveste\n", i, a_rule->in_out);
           continue;
       }
	   else 
	   {
           //verifca protocol
           if ((a_rule->proto==1) && (ip_header->protocol != 6)) 
		   {
               printk(KERN_INFO "regula %d nu se potriveste \n", i);
               continue;
           } 
		   else 
			   if ((a_rule->proto==2) && (ip_header->protocol != 17)) 
			   {
				   printk(KERN_INFO "regula %d nu se potriveste \n", i);
				   continue;
			   }
           //verifica adresa ip
           if (a_rule->src_ip==0) 
		   {
              //regula nu specifica ip-ul, deci se potrivest
           }
		   else 
		   {
              if (!check_ip(src_ip, a_rule->src_ip, a_rule->src_netmask)) 
			  {
                  printk(KERN_INFO "regula %d nu se potriveste: nu se potriveste cu ip-ul sursa\n", i);
                  continue;
              }
           }
           if (a_rule->dest_ip == 0) 
		   {
               //regula nu specifica ip-ul, deci potrivire.
           } 
		   else 
		   {
               if (!check_ip(dest_ip, a_rule->dest_ip, a_rule->dest_netmask)) 
			   {
                   printk(KERN_INFO "regula %d nu se potriveste\n", i);
                   continue;
               }
           }
           //verifica numarul portului.
           if (a_rule->src_port==0)
		   {
               //regula nu specifica numarul portului, deci se potriveste.
           }
		   else
				if (src_port!=a_rule->src_port) 
				{
					   printk(KERN_INFO "regula %d nu se potriveste\n", i);
					   continue;
				}
           if (a_rule->dest_port == 0) 
		   {
               //regula nu specifica numarul portului, deci se potriveste
           }
           else 
			   if (dest_port!=a_rule->dest_port)
			   {
				   printk(KERN_INFO "regula %d nu se potriveste\n", i);
				   continue;
			   }
           //a fost gasita o potrivire, actioneaza conform campului action: BLOCK, UNBLOCK
           if (a_rule->action==0)
		   {
              printk(KERN_INFO "s-a facut o potrivire: %d arunca pachetul\n", i);
              printk(KERN_INFO "---------------------------------------\n");
              return NF_DROP;
           }
		   else 
		   {
              printk(KERN_INFO "s-a facut o potrivire: %d accepta pachetul\n", i);
              printk(KERN_INFO "---------------------------------------\n");
              return NF_ACCEPT;
           }
       }
   }
   printk(KERN_INFO "nicio potrivire, accepta pachetul\n");
   printk(KERN_INFO "---------------------------------------\n");
   return NF_ACCEPT;            
}
